home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr05
/
xnot12a.zip
/
XWIN.C
< prev
Wrap
C/C++ Source or Header
|
1993-06-13
|
34KB
|
1,292 lines
#ifdef X11 /** WHOLE FILE **/
/*
This is a quickie port of a MsWindows version of MicroEMACS to
X11 (Un*x flavor). The original code ran under DOS to a bios interface
with direct hardware access to the keyboard and system clock. This
version and the following X files have been (mostly) hooked in underneath
the original code, which thankfully had most of the terminal io (screen
access) and keyboard input at a level distinct from the core editor. Not
every function is relegated to the absolute best .c file location (for
example, some system calls are implemented inside xwin.c vs unix.c for making
shell commands). In addition, much bashing was done to make
this Emacs look more like GNU-Emacs (make/next-error functions), automatic
timed backups, timestamp checking, and a large assortment of functions
implemented to extend the editor and move it away from MicroEMACS to
GNU-emacs feel. Hopefully a reasonable result given the amount of time
spent.
The goal was not to make emacs really window-aware. Instead, the window
specific code was to act like a terminal. That is why the startup code (X11)
executes first; to create a window, etc such that when EmaxMain is called,
the 'terminal' (ie window) exists. The input handling is done by creating
an internal queue at the 'terminal' level and emacs just reads chars from
the 'keyboard' buffer. Special window-specific things happen without core
code being aware (ie focus events, etc). Events which require a core code
response, like window resize, generate emacs recognizable keycodes (or
entire command strings) which the editor can get. Resize in specific
generates redraw-display and the core code already did a terminal size
query for that command.
Mouse events are funny; window specific code handles them as far as
determining double click, etc. But window level code only goes as far
as converting mouse events to emacs commands for left, right, up/down,
etc. In other words, emacs is now mouse-aware, but in an abstract way.
A wayward user can use the keyboard to send mouse actions, though not
easily. Only window level code can parse a mouse click/motion to rows
and columns that emacs needs. This is based on font, window size, etc.
Note that there are typedefs and defines which look like MS Windows
things; I ported the Dos version to Windows 3.1 first and moved as much
as I could straight to X11-land. Blame that for the code organization!
X source files:
jam.h version defines, includes, etc
xwin.c (this file) main, event loop, window creation, etc
xio.c (aka ttyio.c) character output, scrolling, etc
xkey.c map keypress event to some EMACS virtual key
or extended commands
unix.c not X11 at all, but some un*x flavored things
*/
#define XWIN_C /* for extern of globals */
#include "jam.h"
#include "def.h"
#include "keyname.h"
#include "stdio.h"
#include "ttydef.h"
#include "chrdef.h"
#include "time.h"
#include "keysym.h"
#ifdef HP
# include "HPkeysym.h"
#endif
#ifdef INTERACTIVE
# include "bsdtypes.h"
# define pid_t int /* major ughlyness due to include bug */
#endif
#include <sys/time.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#if defined(SOL) || defined (HP)
# include <sys/resource.h>
struct rlimit rlim;
#endif
/* Globals for all of emacs
*/
Display *gDpy = 0;
Window gWindow = 0;
XFontStruct *gFont = 0;
XCharStruct *gXchar = 0;
GC gText = NULL;
GC gTouchedText = NULL;
GC gMode = NULL;
GC gXor = NULL;
int g_nLineHeight, g_nCharWidth, g_nLineAscent;
BOOL g_hasFocus = FALSE;
BOOL edInited = FALSE;
int g_caret = 0;
int g_caret_x, g_caret_y;
/* Local variables
*/
static XEvent s_event;
static Cursor s_hand = 0, s_pointer = 0, s_wait = 0, s_size = 0, s_arrow;
static char s_timestr[35] = {0};
static BOOL s_btndown = FALSE;
static BOOL s_fatal = FALSE;
static int s_sTicks = 0; /* inc-save interval */
static char *blank = " ";
static char *fontname = def_fontname;
static char *display = 0;
static int s_scrollTicks = 0; /* autoscroll */
static pid_t child = (pid_t) -1;
static BOOL logready = FALSE;
static unsigned long s_textcolor, s_touchedtextcolor, s_windowcolor;
static char *tcolor = NULL, *ccolor = NULL, *wcolor = NULL;
static Colormap s_cmap;
static int s_screen;
/* Local functions
*/
static void rn_(SetTheTitle, (void));
static void rn_(GetTheTime, (void));
static void rn_(InitX, (int ac, char **av, int *nac, char ** nav));
static int rn_(DoGCs, (void));
static void rn_(DoTimer, (void));
static void rn_(use, (void));
static void *rn_(childdone, (void));
static void rn_(getjoblog, (void));
static int rn_(newcolor, (int which));
static void rn_(ReverseCaret, (GC gc));
/* Which mouse button - Note LEFT is button 1 and RIGHT is button 2!!!!
* because this code migrated from Windows where 2 button mouses are the
* norm (yes, it is icky).
*/
#define LEFT 0x1
#define RIGHT 0x2
BOOL WindowFatalState()
{
return(s_fatal);
}
static int errorhandler(dpy, event)
Display *dpy;
XErrorEvent *event;
{
#define ErrorLen 128
char msg[ErrorLen];
XGetErrorText(dpy, event->error_code, msg, ErrorLen);
printf("Xlib error: request %d minor %d \n\t%s\n",
event->request_code, event->minor_code, msg);
ttbeep();
}
static int ioerrorhandler(dpy)
Display *dpy;
{
if (anycb(ABORT))
printf("Fatal XIO error, flushing changes!\n");
ealtmsg = TRUE;
s_fatal = TRUE;
IncrementalSave();
exit (-1); /* we're dead anyway */
}
/* X support functions
*/
int NewFont(f, n)
int f, n;
{
XFontStruct *font;
char newname[NFILEN*2];
int s;
s = eread("New font name:", newname, NFILEN*2, EFNEW);
if (s != TRUE)
return (s);
if (!(font = XLoadQueryFont(gDpy,newname)))
{
ewprintf("Can't load requested font %s.", newname);
s = ABORT;
}
else
{
XFreeFont(gDpy, gFont); /* release old font */
gFont = font; /* replace the font */
#if 1 /* easier this way */
DoGCs(); /* reset the GC values */
#else
gXchar = &gFont->max_bounds; /* recompute all info */
gXchar = &gFont->max_bounds;
g_nLineHeight = gFont->ascent + gFont->descent + FudgeHeight;
if (g_nLineHeight % 2)
g_nLineHeight++; /* force even # pixels */
g_nCharWidth = gXchar->width;
XSetFont(gDpy, gMode, gFont->fid); /* update gc's */
XSetFont(gDpy, gText, gFont->fid);
XSetFont(gDpy, gTouchedText, gFont->fid);
XSetFont(gDpy, gXor, gFont->fid); /* overkill,cursor gc only */
#endif
myrefresh(0, 1);
WindowSync();
}
}
int NewTextColor(f, n)
int f, n;
{
return(newcolor(CTEXT));
}
int NewTouchedTextColor(f, n)
int f, n;
{
return(newcolor(CHIGH));
}
int NewWindowColor(f, n)
int f, n;
{
int s = newcolor(CMODE);
if (s)
XSetWindowBackground(gDpy, gWindow, s_windowcolor);
return(s);
}
/* work function for color changing
*/
static int newcolor(type)
int type;
{
char color[NFILEN*2];
int s;
XColor colorcell, rgb;
char *msg;
if (type == CTEXT)
msg = "New text color: ";
else if (type == CHIGH)
msg = "New touched text color: ";
else if (type == CMODE)
msg = "New window color: ";
s = eread(msg, color, NFILEN*2, EFNEW);
if (s != TRUE)
return (s);
if (!XAllocNamedColor(gDpy, s_cmap, color, &colorcell, &rgb))
ewprintf("Unable to allocate color");
else
{
if (type == CTEXT)
s_textcolor = colorcell.pixel;
else if (type == CHIGH)
s_touchedtextcolor = colorcell.pixel;
else if (type == CMODE)
s_windowcolor = colorcell.pixel;
else
{
ewprintf("Internal error changing colors");
return(FALSE);
}
DoGCs();
myrefresh(0, 1);
WindowSync();
}
return(s);
}
/* Main entry point, ta da!
*/
static char *whoami;
main(argc, argv)
int argc;
char *argv[];
{
#define MAXARGS 500
int myargc = 0;
char *myargv[MAXARGS];
XColor colorcell, rgb;
/* Open the device based on user input; create
* drawing context and open a default font
*/
whoami = argv[0]; /* progname */
if (argc > MAXARGS)
{
printf("Number of input args too large! Truncating to %d\n", MAXARGS);
argc = MAXARGS;
}
InitX(argc, argv, &myargc, myargv);
/* Init colors, drawing GC's
*/
if (tcolor && XAllocNamedColor(gDpy, s_cmap, tcolor, &colorcell, &rgb))
s_textcolor = colorcell.pixel;
else
s_textcolor = BlackPixel(gDpy, s_screen);
if (ccolor && XAllocNamedColor(gDpy, s_cmap, ccolor, &colorcell, &rgb))
s_touchedtextcolor = colorcell.pixel;
else
s_touchedtextcolor = BlackPixel(gDpy, s_screen);
if (wcolor && XAllocNamedColor(gDpy, s_cmap, wcolor, &colorcell, &rgb))
s_windowcolor = colorcell.pixel;
else
s_windowcolor = WhitePixel(gDpy, s_screen);
DoGCs();
/* Cursors (mouse pointers...)
*/
s_hand = XCreateFontCursor(gDpy, XC_xterm);
s_pointer = XCreateFontCursor(gDpy, XC_top_left_arrow);
s_wait = XCreateFontCursor(gDpy, XC_watch);
s_size = XCreateFontCursor(gDpy, XC_double_arrow);
s_arrow = XCreateFontCursor(gDpy, XC_question_arrow);
/* Initially size window based on font
*/
InitSizeWindow(ncol = START_COLS, nrow = START_LINES);
/* Init internal input buffer(s)
*/
InitInput();
/* Set signals to be ignored
*/
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* Capture errors.
*/
XSetIOErrorHandler(ioerrorhandler); /* fatal */
XSetErrorHandler(errorhandler); /* non fatal */
/* Default title, make window visible, set cursor...
*/
WindowNormalCursor();
XStoreName(gDpy, gWindow, g_APPNAME);
XMapWindow(gDpy, gWindow);
XSync(gDpy, TRUE); /* sync, throw away all previous events */
/*
* Call 'main' body of editor. Note it trys to open the
* terminal, etc but those functions are now noops..
*/
edInited = TRUE; /* any message after this is ok to process */
EmaxMain(myargc, myargv);
return(0);
}
/* Create/modify gcs for CTEXT, CHIGH and CMODE colors and cursor;
* This will sometimes unnecessarily reset some values but it's easier
* to capture all the code in one place.
*/
static int DoGCs()
{
XGCValues _gcv, *gcv;
XVisualInfo vinfo;
/* This will only abort the editor if called the
* first time; after that, there should be a font
*/
if (!gFont)
if (!(gFont = XLoadQueryFont(gDpy, fontname)))
{
printf("Can't load font %s, trying 'fixed'\n", fontname);
if (!(gFont = XLoadQueryFont(gDpy, "fixed")))
{
printf("Can't load fixed font\n");
use();
exit(-1);
}
}
gcv = &_gcv;
gXchar = &gFont->max_bounds;
g_nCharWidth = gXchar->width;
g_nLineHeight = gFont->ascent + gFont->descent + FudgeHeight;
/* GC for text
*/
gcv->foreground = s_textcolor;
gcv->background = s_windowcolor;
gcv->function = GXcopy;
gcv->font = gFont->fid;
if (!gText)
gText = XCreateGC(gDpy, gWindow,
GCForeground|GCBackground|GCFunction|GCFont, gcv);
else
XChangeGC(gDpy, gText, GCForeground|GCBackground|GCFunction|GCFont, gcv);
/* GC for window background
*/
gcv->foreground = s_windowcolor;
gcv->background = s_textcolor;
gcv->function = GXcopy;
gcv->font = gFont->fid;
if (!gMode)
gMode = XCreateGC(gDpy, gWindow,
GCForeground|GCBackground|GCFunction|GCFont, gcv);
else
XChangeGC(gDpy, gMode, GCForeground|GCBackground|GCFunction|GCFont, gcv);
/* GC for touched text
*/
gcv->foreground = s_touchedtextcolor;
gcv->background = s_windowcolor;
gcv->function = GXcopy;
gcv->font = gFont->fid;
if (!gTouchedText)
gTouchedText = XCreateGC(gDpy, gWindow,
GCForeground|GCBackground|GCFunction|GCFont, gcv);
else
XChangeGC(gDpy, gTouchedText, GCForeground|GCBackground|GCFunction|GCFont,
gcv);
/* GC for XOR
*/
gcv->foreground = s_textcolor;
gcv->background = s_windowcolor;
/* This is not a good test; but invert works pretty
* well on the color and monochrome servers I tried, and xor works
* much better on the grayscale SPARCstation...
*/
# ifdef SOL
if (!XMatchVisualInfo(gDpy, s_screen, 8, StaticGray, &vinfo) &&
!XMatchVisualInfo(gDpy, s_screen, 8, GrayScale, &vinfo))
# else
if (1)
# endif
gcv->function = GXinvert;
else
gcv->function = GXxor;
gcv->plane_mask = XAllPlanes();
gcv->font = gFont->fid;
if (!gXor)
gXor = XCreateGC(gDpy, gWindow,
GCForeground|GCBackground|GCFunction|GCPlaneMask|GCFont,
gcv);
else
XChangeGC(gDpy, gXor,
GCForeground|GCBackground|GCFunction|GCPlaneMask|GCFont, gcv);
}
/* (Re)Set clipping into all GC's.
*/
void NewClipping (x, y, w, h)
int x, y, w, h;
{
static XRectangle clip; /* probably doesn't need to be static */
/* Some servers seem to croak on negatives..
*/
clip.x = (x < 0 ? 0 : x);
clip.y = (y < 0 ? 0 : y);
clip.width = (w <= 0 ? WinWidth() : w);
clip.height = (h <= 0 ? WinHeight() : h);
XSetClipRectangles(gDpy, gText, 0, 0, &clip, 1, Unsorted);
XSetClipRectangles(gDpy, gTouchedText, 0, 0, &clip, 1, Unsorted);
XSetClipRectangles(gDpy, gXor, 0, 0, &clip, 1, Unsorted);
XSetClipRectangles(gDpy, gMode, 0, 0, &clip, 1, Unsorted);
}
/* Open the display, parse arguments as needed
* and prepare them for core code.
*/
static void InitX(argc, argv, newargc, newargv)
int argc, *newargc;
char *argv[], *newargv[];
{
unsigned long x_mask;
int height,width,border;
int offset;
XWMHints hints;
int i, j;
display = (char *)getenv("DISPLAY");
/* Cheapo argument parsing... look for font override and/or
* display override. All else packages for core editor to eat.
*/
for (j = i = 0; i < argc; i++)
{
if ((strcmp(argv[i], "-font") == 0) && (i + 1 < argc))
{
i++;
fontname = argv[i];
}
else if ((strcmp(argv[i], "-display") == 0) && (i + 1 < argc))
{
i++;
display = argv[i];
}
else if ((strcmp(argv[i], "-fg") == 0) && (i + 1 < argc))
{
i++;
tcolor = argv[i];
}
else if ((strcmp(argv[i], "-fg2") == 0) && (i + 1 < argc))
{
i++;
ccolor = argv[i];
}
else if ((strcmp(argv[i], "-bg") == 0) && (i + 1 < argc))
{
i++;
wcolor = argv[i];
}
else
newargv[j++] = argv[i];
}
*newargc = j; /* number of args */
if ((gDpy = XOpenDisplay(display)) == NULL)
{
printf("Can not open display [%s]", display);
use();
exit(-1);
}
/* The one and only window.
*/
x_mask = EnterWindowMask | LeaveWindowMask | ButtonPressMask | ExposureMask |
ButtonReleaseMask | KeyPressMask | KeyReleaseMask | FocusChangeMask |
Button1MotionMask | StructureNotifyMask | SubstructureNotifyMask;
/* Create the window - setting white/black
* (this is a dummy temp size which will be reset after the
* font is loaded).
*/
width = 10;
height = 10;
border = 1;
s_screen = DefaultScreen(gDpy);
s_cmap = DefaultColormap(gDpy, s_screen);
gWindow = XCreateSimpleWindow(gDpy, RootWindow(gDpy, s_screen),
10, 50, width, height, border,
BlackPixel(gDpy, s_screen),
WhitePixel(gDpy, s_screen));
/* Let the windows get some events
*/
XSelectInput(gDpy, gWindow, x_mask);
/* Window manager stuff. More complete window manager
* hints would be nice.
*/
hints.flags = InputHint;
hints.input = 1;
XSetWMHints(gDpy, gWindow, &hints);
}
/* Set window title as needed.
*/
static void SetTheTitle()
{
char buffer[200], dir[100];
#ifndef HOSTNAMELEN /* doesn't seem to be in a standard .h file! */
# define HOSTNAMELEN 256
#endif
static char hostname[HOSTNAMELEN+1] = {0};
static char lastTitle[512] = {0};
if (!hostname[0])
gethostname(hostname, HOSTNAMELEN);
strcpy(buffer, g_APPNAME);
strcat(buffer, " ");
strcat(buffer, shortversion());
if (wdir)
strcpy(dir, wdir);
adjustnamecase(dir);
if (hostname[0])
{
strcat(buffer, "@");
strcat(buffer, hostname);
}
strcat(buffer, " (");
strcat(buffer, dir);
strcat(buffer, ") ");
strcat(buffer, s_timestr);
if (strcmp(buffer, lastTitle) != 0)
{
XStoreName(gDpy, gWindow, buffer);
strcpy(lastTitle, buffer); /* save last time */
WindowSync();
}
}
/* Update the time string, used for window title
*/
static void GetTheTime()
{
long currtime;
struct tm *timestruct;
time(&currtime);
timestruct = localtime(&currtime);
sprintf(s_timestr, " %02d:%02d\0", timestruct->tm_hour, timestruct->tm_min);
}
/* Event during screen update? (ie redraw/refresh)
*/
BOOL WindowLookaheadEvent()
{
return(FALSE); /* no event considered as interrupting */
}
/* Dump a string to a message box; if fatal,
* leave ungracefully...
*/
void WindowMessage(s, fatal)
char *s;
BOOL fatal;
{
printf("%s\n", s); /* well, the terminal is ok for X */
if (fatal)
{
s_fatal = TRUE;
ealtmsg = TRUE;
IncrementalSave();
exit(-1);
}
}
/* Simple-minded stub to exec something
*/
winspawn(s, windows)
char *s;
BOOL windows; /* misnamed - means peer job not monitored to completion */
{
char buffer[NFILEN + L_tmpnam + 1];
pid_t parent = getpid();
pid_t oldchild = child;
#ifndef NJBOS
if (!windows)
if (child != (pid_t)-1)
{
ewprintf("Can't run mulitple shell commands at this time!");
return(FALSE);
}
#endif
if (s && *s)
{
#ifndef JOBS
/* Make a log file for this parent process to
* use for all shell jobs
*/
if (spawnfilename[0] == '\0')
#endif
{
strcpy(spawnfilename, mktempTemplate);
mktemp(spawnfilename);
}
if (!windows)
{
sprintf(buffer, "echo '%s shell..' > %s", g_APPNAME, spawnfilename);
system(buffer);
}
strcpy(buffer, s);
if (!windows)
{
strcat(buffer, " 2>> "); /* concatenate... */
strcat(buffer, spawnfilename);
}
else
strcat(buffer, "&");
ewprintf("Running 'sh %s'", buffer);
/* Fork a child to run the system call.
* Child just exits when done, parent
* watches for the signal.
*/
if (!windows)
signal(SIGUSR1, childdone); /* wait for child-is-done signal */
if ((child = fork()) != (pid_t)-1)
{
if (child == 0)
{
if (system(buffer) == 127)
printf("'PID %d system call error!\n", getpid());
if (!windows)
{
#ifndef SOL /* This is weird.... */
kill(parent, SIGUSR1);
#else
sigsend(P_PID, parent, SIGUSR1); /* tell parent */
#endif
}
_exit(0);
}
}
#ifndef NJOBS
if (windows)
child = oldchild; /* restore state of any current job */
#endif
}
return(TRUE);
}
/* Captures signal to indicate
* child shell process is complete
*/
static void *childdone()
{
logready = TRUE;
}
static void getjoblog()
{
int r;
logready = FALSE;
r = eyesno("Shell command completed, read log");
if (r == TRUE)
{
BUFFER *bp;
#ifndef NJOBS
bp = bfind(spawnfilename, FALSE);
/* Nuke any old logs from different dirs
*/
if (bp)
{
bp->b_flag &= ~BFCHG;
if (strcmp(bp->b_fname, spawnfilename) != 0)
nukebuffer(bp);
}
#endif
#if 1
ExtendedFunction(function_name(poptofilequiet));
AddString(spawnfilename);
AddKchar(CCHR('J'));
#else
AddString(spawnfilename);
AddKchar(CCHR('J'));
poptofilequiet(0, 1); /* go get it */
ExtendedFunction(function_name(donothing));
/* Mark the file to be deleted when buffer is
* nuked
*/
if (bp = bfind(spawnfilename, FALSE))
bp->b_flag |= BFDELETE;
#endif
}
child = (pid_t)-1; /* child is done */
}
/* Find row col from pos
*/
void GetRowCol(prow, pcol, x, y)
int *prow;
int *pcol;
int x;
int y;
{
register int row, col;
col = (x + 1)/g_nCharWidth;
row = (y + 1)/g_nLineHeight;
if (col < 0)
col = 0;
else if (col > ncol)
col = ncol;
if (row < 0)
row = 0;
else if (row > nrow)
row = nrow;
*pcol = col;
*prow = row;
}
/* Draw cursor; s_cur_* set on draw, used on un-draw.
* Needed because ttmove moves g_caret_* w/o turning
* off caret.
*/
static int s_cur_x = -1, s_cur_y = -1;;
static char s_cur_char;
void DoShowCaret()
{
MakeCaretVis(TRUE);
s_cur_x = g_caret_x;
s_cur_y = g_caret_y;
#if 1
if (curwp->w_doto >= llength(curwp->w_dotp))
s_cur_char = ' ';
else
{
s_cur_char = lgetc(curwp->w_dotp, curwp->w_doto);
if ((s_cur_char < XK_space) || (s_cur_char > XK_asciitilde))
{
if (s_cur_char == '\t')
s_cur_char = ' ';
else if (ISCTRL(s_cur_char) && !eprompting)
s_cur_char = '^';
else
s_cur_char = ' ';
}
else if (eprompting && !isearching)
s_cur_char = ' ';
}
#endif
ReverseCaret(gMode);
}
/* Erase cursor
*/
void DoHideCaret()
{
GC gctemp = (showtouchedlines && curwp->w_dotp->l_flag & LFCHANGE) ?
gTouchedText : gText;
MakeCaretVis(FALSE);
ReverseCaret(gctemp);
}
/* Do draw for cursor shape
*/
static void ReverseCaret(gc)
GC gc;
{
if ((s_cur_x == -1) || (s_cur_y == -1))
return;
#if 1
XDrawImageString(gDpy, gWindow, gc, s_cur_x, yCharPos(s_cur_y),
&s_cur_char, 1);
#else
XFillRectangle(gDpy, gWindow, gXor, s_cur_x, s_cur_y,
(unsigned int)g_nCharWidth,
(unsigned int)(g_nLineHeight - FudgeHeight));
#endif
WindowFlush();
}
/* Manage pointer-cursor switching for some operations
*/
void WindowDragCursor()
{
XDefineCursor(gDpy, gWindow, s_hand);
WindowFlush();
}
void WindowArrowCursor()
{
XDefineCursor(gDpy, gWindow, s_arrow);
WindowFlush();
}
void WindowSizeCursor()
{
XDefineCursor(gDpy, gWindow, s_size);
WindowFlush();
}
void WindowSleepCursor()
{
if (s_fatal)
return;
XDefineCursor(gDpy, gWindow, s_wait);
WindowFlush();
}
void WindowNormalCursor()
{
if (s_fatal)
return;
XDefineCursor(gDpy, gWindow, s_pointer);
WindowFlush();
}
/* Main event loop/processing; called from core editor to
* get 'keyboard' event. Returns only when a queued event
* results from some X event. (Note the use of 'noop()' to
* effect a return).
*/
#define OneClick 250 /* get from resource file ! */
#define IsLeft (b->button == LEFT)
#define IsRight (b->button == RIGHT)
#define IsDoubleClick (!IsLeftShift && (b->time - lastdown < OneClick))
#define IsLeftShift (IsLeft && GetShiftState(&s_event))
void WindowGetEvent(ptr)
KCHAR *ptr;
{
static BOOL hackflag = FALSE;
BOOL result;
static Time lastdown = 0;
char buff[40];
int downcol, downrow;
XButtonEvent *b = (XButtonEvent *)&s_event;
XMotionEvent *m = (XMotionEvent *)&s_event;
XExposeEvent *e = (XExposeEvent *)&s_event;
BOOL wasvis = IsCaretVis();
/* Cursor on during wait.
*/
if (IsCaretCreated() && (s_btndown == 0))
SetCaretVis((wasvis = TRUE));
/* See if something there, else wait.
*/
for (result = FALSE; !result; )
if (!(result = WindowReturnKCHAR(ptr)))
{
int selreturn = 0;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = TIME_INC;
/* Use select call to block waiting for new events
* IFF no events already queued. XNextEvent will flush
* output, but because select is used to simulate a timer as
* well as process-block for i/o, an explicit flush is needed.
* So, select will block until the connection has i/o or the
* timeout value (set above) expires.
*/
WindowFlush();
if (XEventsQueued(gDpy, QueuedAlready))
selreturn = 1;
else
{
fd_set fds;
/* hack for no refresh when needed
*/
if (hackflag)
{
NoClipping();
ExtendedFunction(function_name(myrefresh));
hackflag = FALSE;
goto out; /* ughly */
}
FD_ZERO(&fds);
FD_SET(ConnectionNumber(gDpy), &fds);
#if defined(SOL) || defined(HP)
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
{
selreturn = select(rlim.rlim_cur, &fds, 0, 0, &tv);
}
else
{
ewprintf("getrlimit failed!");
ttbeep();
continue;
}
#else
selreturn = select(getdtablesize(), &fds, 0, 0, &tv);
#endif
}
/* 'selreturn' indicates state; events waiting (ie previous
* events or new), the timer 'ticked' or an error occurred.
*/
if (selreturn < 0)
{
ttbeep();
continue; /* what else to do? try again */
}
/* NULL means timer expired w/o traffic on socket
*/
else if (selreturn == 0)
DoTimer();
/* Process and return if result of this event
* yields a KCHAR. To get here means XNextEvent
* will not block because something is in the queue.
*/
else
{
XNextEvent(gDpy, &s_event);
switch(s_event.type)
{
case FocusIn:
SetCaretCreated(TRUE);
SetCaretVis(TRUE);
g_hasFocus = TRUE;
break;
case FocusOut:
#if 0
SetCaretVis(FALSE);
SetCaretCreated(FALSE);
#endif
g_hasFocus = FALSE;
break;
case ConfigureNotify:
{
int save_ncol = ncol;
int save_nrow = nrow;
WindowSync();
WindowGetSize(&nrow, &ncol);
if ((nrow == save_nrow) && (ncol == save_ncol))
break;
/* BUG PRONE - this is basically doing
* a hack to get a resize but not draw
* a zillion times; most every resize gets
* a corresponding refresh. Note the
* hack flag...
*/
NewClipping(0, 0, 1, 1);
hackflag = TRUE;
ExtendedFunction(function_name(myrefresh));
break;
}
case Expose:
case GraphicsExpose:
{
int row = ttrow; /* save cursor */
int col = ttcol;
/* clip to damaged area, repair window..
* and restore clipping
*/
NewClipping(e->x, e->y, e->width, e->height);
if (wasvis)
SetCaretVis(FALSE);
sgarbf = TRUE;
update();
if (wasvis)
SetCaretVis(TRUE);
NoClipping();
hackflag = FALSE;
if (isearching)
noop(); /* jumpSearch(); hacky research to refresh */
else if (eprompting)
erepair(); /* fix prompt+msg */
else
ExtendedFunction(function_name(donothing));
ttmove(row, col); /* restore cursor */
break;
}
case MotionNotify:
GetRowCol(&downrow, &downcol, m->x, m->y);
if ((s_btndown & LEFT) && !(s_btndown & RIGHT))
{
ExtendedFunction(function_name(mousecmd));
AddString(MoveStr);
sprintf(buff, posFormat, downrow, downcol);
AddString(buff);
}
break;
case ButtonRelease:
GetRowCol(&downrow, &downcol, b->x, b->y);
if (IsLeft && (s_btndown & LEFT))
{
s_btndown &= ~LEFT;
ExtendedFunction(function_name(mousecmd));
AddString(LeftUp);
sprintf(buff, posFormat, downrow, downcol);
AddString(buff);
}
else if (IsRight && (s_btndown & RIGHT))
{
s_btndown &= ~RIGHT;
noop();
}
break;
case ButtonPress:
GetRowCol(&downrow, &downcol, b->x, b->y);
switch(b->button)
{
case RIGHT:
s_btndown |= RIGHT;
ExtendedFunction(function_name(mousecmd));
if (s_btndown & LEFT)
{
AddString(MouseAbort);
AddKchar(' ');
}
else
{
AddString(RightDwn);
sprintf(buff, posFormat, downrow, downcol);
AddString(buff);
}
break;
case LEFT:
s_btndown |= LEFT;
ExtendedFunction(function_name(mousecmd));
if (s_btndown & RIGHT)
{
AddString(MouseAbort);
AddKchar(' ');
}
else
{
/* doubleclick left is special
*/
if (IsDoubleClick)
{
AddString(DoubleClick);
AddKchar(' ');
lastdown = b->time;
break; /* no other params! */
}
else if (IsLeftShift)
AddString(LeftDwnShift);
else /* normal left */
AddString(LeftDwn);
sprintf(buff, posFormat, downrow, downcol);
AddString(buff);
lastdown = b->time;
}
break;
default:
ewprintf("Unsupported button");
ttbeep();
break;
}
break;
case KeyRelease:
case KeyPress:
/* Keyboard input events cause some delay to autosave
*/
if (s_event.type == KeyPress)
{
s_sTicks -= SAVE_DELAY;
if (s_sTicks < 0)
s_sTicks = 0;
}
/* prevent garbled commands interrupting mouse
*/
if (!s_btndown)
WindowMapKey((XKeyEvent *)&s_event, s_event.type == KeyPress);
break;
default:
break;
} /* switch */
} /* XNextEvent */
} /* for () */
/* cursor off during process
*/
out: /* I hate goto's.... */
if (wasvis)
SetCaretVis(FALSE);
}
/* Process a 'timer' message; really result of timeout
* on select call as opposed to actual event.
*/
static void DoTimer()
{
static int tTicks = 0; /* Title update interval */
static BOOL incallback = FALSE; /* Prevent recursion in WM_TIMER
triggered routine calls */
if (edInited)
{
/* Log ready? Try to be polite about interrupting the
* user. (Ughly code here!)
*/
if (!isearching && !eprompting && !s_btndown)
{
/* What time is it?
*/
if (!incallback)
{
incallback = TRUE;
AnyPendingAlarms();
incallback = FALSE;
}
/* spawned job ran
*/
if (logready)
{
SetCaretVis(FALSE);
getjoblog();
noop();
}
}
/* counter for incremental save (keyboard input delays this)
*/
s_sTicks++;
if ((s_sTicks > INCS_PER_SAVE) && !incallback && !eprompting)
{
/* Any messages printed by routines called
* from a timer must not use standard
* output method (ie async)
*/
ealtmsg = TRUE;
incallback = TRUE;
IncrementalSave();
incallback = FALSE;
s_sTicks = 0;
ealtmsg = FALSE;
}
/* Counter for titlebar update
*/
tTicks++;
if (tTicks > INCS_PER_UPDATE)
{
GetTheTime(); /* get current clock time */
SetTheTitle(); /* fix window title */
tTicks = 0;
}
/* auto scroll mode line
*/
if ((s_btndown & LEFT) && GetCtrlState(&s_event))
{
s_scrollTicks++;
if (s_scrollTicks >= DELAY_TO_SCROLL)
{
char buff[8];
ExtendedFunction(function_name(mousecmd));
AddString(MouseTimer);
sprintf(buff, " %d ", GetShiftState(&s_event));
AddString(buff);
s_scrollTicks = DELAY_TO_SCROLL; /* prevent overflow */
}
}
else
s_scrollTicks = 0; /* force reset */
/* Mail status changed?
*/
anynewmail();
}
}
void WindowSync()
{
XSync(gDpy, FALSE);
}
void WindowFlush()
{
ttcharflush();
XFlush(gDpy);
}
static void use()
{
printf("\nusage: %s [-font font] [-display dpy] [-fg] [-fg2] [-bg] [files]\n",
whoami);
}
/* sleep hack - not so long..
*/
unsigned sleep(x)
unsigned int x;
{
/* x is number of seconds; this is a major hack to
* wait for a timeout; note NO real fd's used. This works
* because no i/o can normally occur to cause a wakeup,
* and the timeout value can be tuned so sleeping isn't such a
* lengthy process (ie x=1 is about 3/4 of a sec).
*/
int selreturn = 0;
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = x * TIME_INC;
FD_ZERO(&fds);
#if defined(SOL) || defined(HP)
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
selreturn = select(rlim.rlim_cur, &fds, 0, 0, &tv);
else
{
ewprintf("getrlimit failed!");
ttbeep();
}
#else
selreturn = select(getdtablesize(), &fds, 0, 0, &tv);
#endif
}
#endif /** JAM **/